
package asteval

import (
	"math/big"
	"environ"
)

const (
  number byte = iota
  variable
  unary
  binary
)

type Expression struct {
  class byte
  value *big.Int
  varname string
  operation string
  first, second *Expression
}

func Number(num string) *Expression {
  nm := new(big.Int)
  nm.SetString(num, 10)
  return &Expression{class: number, value: nm}
}

func Variable(nam string) *Expression {
  return &Expression{class: variable, varname: nam}
}

func Unary(op string, ex *Expression) *Expression {
  return &Expression{class: unary, operation: op, first: ex}
}

func Binary(op string, exo, exi *Expression) *Expression {
  return &Expression{class: binary, operation: op, first: exo, second: exi}
}

func (e *Expression) Evaluate(env *environ.Environment) *big.Int {
  var res *big.Int = new(big.Int)

  switch e.class {
    case number:
      res.Set(e.value)
    case variable:
      res.Set(env.Get(e.varname))
    case unary:
      res = e.first.Evaluate(env)
      if "-" == e.operation { res.Neg(res) }
    case binary:
      exo := e.first.Evaluate(env)
      exi := e.second.Evaluate(env)
      switch e.operation {
        case "+":
          res.Add(exo, exi)
        case "-":
          res.Sub(exo, exi)
        case "*":
          res.Mul(exo, exi)
        case "/":
          res.Div(exo, exi)
        case "=":
          u := exo.Cmp(exi)
          res.SetInt64(toInt64(u == 0))
        case "<>":
          u := exo.Cmp(exi)
          res.SetInt64(toInt64(u != 0))
        case ">":
          u := exo.Cmp(exi)
          res.SetInt64(toInt64(u == 1))
        case ">=":
          u := exo.Cmp(exi)
          res.SetInt64(toInt64(u != -1))
        case "<":
          u := exo.Cmp(exi)
          res.SetInt64(toInt64(u == -1))
        case "<=":
          u := exo.Cmp(exi)
          res.SetInt64(toInt64(u != 1))
      }
  }

  return res
}

func toInt64(b bool) int64 {
  if b { return 1 }
  return 0
}

